রিঅ্যাক্ট ফাইবারের প্রায়োরিটি লেন ম্যানেজমেন্টে দক্ষতা অর্জন করে সাবলীল ইউজার ইন্টারফেস তৈরি করুন। কনকারেন্ট রেন্ডারিং, শিডিউলার এবং startTransition-এর মতো নতুন API-এর একটি বিস্তারিত গাইড।
রিঅ্যাক্ট ফাইবার প্রায়োরিটি লেন ম্যানেজমেন্ট: রেন্ডারিং নিয়ন্ত্রণের এক গভীর বিশ্লেষণ
ওয়েব ডেভেলপমেন্টের জগতে, ব্যবহারকারীর অভিজ্ঞতা (user experience) সবচেয়ে গুরুত্বপূর্ণ। একটি ক্ষণিকের ফ্রিজ, একটি আটকে যাওয়া অ্যানিমেশন, বা একটি ধীর ইনপুট ফিল্ড একজন আনন্দিত ব্যবহারকারী এবং একজন হতাশ ব্যবহারকারীর মধ্যে পার্থক্য তৈরি করতে পারে। বছরের পর বছর ধরে, ডেভেলপাররা সাবলীল, প্রতিক্রিয়াশীল অ্যাপ্লিকেশন তৈরি করার জন্য ব্রাউজারের সিঙ্গেল-থ্রেডেড প্রকৃতির সাথে লড়াই করে আসছে। রিঅ্যাক্ট ১৬-এ ফাইবার আর্কিটেকচারের প্রবর্তন এবং রিঅ্যাক্ট ১৮-এ কনকারেন্ট ফিচারগুলির সম্পূর্ণ বাস্তবায়নের সাথে, খেলার নিয়ম মৌলিকভাবে পরিবর্তিত হয়েছে। রিঅ্যাক্ট এমন একটি লাইব্রেরি থেকে বিকশিত হয়েছে যা কেবল UI রেন্ডার করে না, বরং বুদ্ধিমত্তার সাথে UI আপডেটগুলি শিডিউল করে।
এই গভীর আলোচনাটি এই বিবর্তনের কেন্দ্রবিন্দু অর্থাৎ রিঅ্যাক্ট ফাইবারের প্রায়োরিটি লেন ম্যানেজমেন্ট অন্বেষণ করবে। আমরা ব্যাখ্যা করব কীভাবে রিঅ্যাক্ট সিদ্ধান্ত নেয় যে কোনটি এখন রেন্ডার করতে হবে, কোনটি অপেক্ষা করতে পারে, এবং কীভাবে এটি ইউজার ইন্টারফেস ফ্রিজ না করে একাধিক স্টেট আপডেট একসাথে সামলায়। এটি শুধু একটি অ্যাকাডেমিক আলোচনা নয়; এই মূল নীতিগুলি বোঝা আপনাকে বিশ্বব্যাপী দর্শকদের জন্য দ্রুত, স্মার্ট এবং আরও স্থিতিস্থাপক অ্যাপ্লিকেশন তৈরি করতে সক্ষম করবে।
স্ট্যাক রিকনসিলার থেকে ফাইবার: নতুন করে লেখার পেছনের 'কেন'
ফাইবারের উদ্ভাবনকে সাধুবাদ জানাতে, আমাদের প্রথমে এর পূর্বসূরি, স্ট্যাক রিকনসিলারের সীমাবদ্ধতাগুলি বুঝতে হবে। রিঅ্যাক্ট ১৬-এর আগে, রিকনসিলিয়েশন প্রক্রিয়া — যে অ্যালগরিদমটি রিঅ্যাক্ট একটি ট্রি-এর সাথে অন্যটির তুলনা করে DOM-এ কী পরিবর্তন করতে হবে তা নির্ধারণ করে — সেটি ছিল সিনক্রোনাস এবং রিকার্সিভ। যখন একটি কম্পোনেন্টের স্টেট আপডেট হতো, রিঅ্যাক্ট পুরো কম্পোনেন্ট ট্রি জুড়ে যেত, পরিবর্তনগুলি গণনা করত এবং সেগুলিকে DOM-এ একটি একক, অবিচ্ছিন্ন ক্রমে প্রয়োগ করত।
ছোট অ্যাপ্লিকেশনের জন্য এটি ঠিক ছিল। কিন্তু গভীর কম্পোনেন্ট ট্রি সহ জটিল UI-এর জন্য, এই প্রক্রিয়াটি যথেষ্ট সময় নিতে পারত — ধরা যাক, ১৬ মিলিসেকেন্ডের বেশি। যেহেতু জাভাস্ক্রিপ্ট সিঙ্গেল-থ্রেডেড, তাই একটি দীর্ঘ সময় ধরে চলা রিকনসিলিয়েশন টাস্ক মূল থ্রেডটিকে ব্লক করে দিত। এর মানে হল ব্রাউজার অন্যান্য গুরুত্বপূর্ণ কাজগুলি পরিচালনা করতে পারত না, যেমন:
- ব্যবহারকারীর ইনপুটের প্রতিক্রিয়া (যেমন টাইপিং বা ক্লিক করা)।
- অ্যানিমেশন চালানো (CSS বা জাভাস্ক্রিপ্ট-ভিত্তিক)।
- অন্যান্য সময়-সংবেদনশীল লজিক কার্যকর করা।
এর ফলাফল ছিল "জ্যাঙ্ক" (jank) নামে পরিচিত একটি ঘটনা — একটি আটকে যাওয়া, অপ্রতিক্রিয়াশীল ব্যবহারকারীর অভিজ্ঞতা। স্ট্যাক রিকনসিলার একটি সিঙ্গেল-ট্র্যাক রেলওয়ের মতো কাজ করত: একবার একটি ট্রেন (একটি রেন্ডার আপডেট) তার যাত্রা শুরু করলে, তাকে শেষ পর্যন্ত চলতে হতো, এবং অন্য কোনো ট্রেন সেই ট্র্যাক ব্যবহার করতে পারত না। এই ব্লকিং প্রকৃতিটিই ছিল রিঅ্যাক্টের মূল অ্যালগরিদম সম্পূর্ণ নতুন করে লেখার প্রধান প্রেরণা।
রিঅ্যাক্ট ফাইবারের পেছনের মূল ধারণাটি ছিল রিকনসিলিয়েশনকে এমন কিছু হিসাবে নতুন করে কল্পনা করা যা ছোট ছোট কাজের খণ্ডে বিভক্ত করা যেতে পারে। একটি একক, মনোলিথিক টাস্কের পরিবর্তে, রেন্ডারিং থামানো, পুনরায় শুরু করা এবং এমনকি বাতিলও করা যেতে পারে। এই সিনক্রোনাস থেকে অ্যাসিনক্রোনাস, শিডিউলযোগ্য প্রক্রিয়ায় স্থানান্তর রিঅ্যাক্টকে ব্রাউজারের মূল থ্রেডে নিয়ন্ত্রণ ফিরিয়ে দেওয়ার অনুমতি দেয়, এটি নিশ্চিত করে যে ব্যবহারকারীর ইনপুটের মতো উচ্চ-অগ্রাধিকারের কাজগুলি কখনও ব্লক হয় না। ফাইবার সিঙ্গেল-ট্র্যাক রেলওয়েকে উচ্চ-অগ্রাধিকারের ট্র্যাফিকের জন্য এক্সপ্রেস লেন সহ একটি মাল্টি-লেন হাইওয়েতে রূপান্তরিত করেছে।
'ফাইবার' কী? কনকারেন্সির বিল্ডিং ব্লক
এর মূলে, একটি "ফাইবার" হলো একটি জাভাস্ক্রিপ্ট অবজেক্ট যা কাজের একটি একককে (unit of work) প্রতিনিধিত্ব করে। এতে একটি কম্পোনেন্ট, তার ইনপুট (props), এবং তার আউটপুট (children) সম্পর্কে তথ্য থাকে। আপনি একটি ফাইবারকে একটি ভার্চুয়াল স্ট্যাক ফ্রেম হিসাবে ভাবতে পারেন। পুরানো স্ট্যাক রিকনসিলারে, ব্রাউজারের কল স্ট্যাকটি রিকার্সিভ ট্রি ট্রাভার্সাল পরিচালনা করতে ব্যবহৃত হতো। ফাইবারের সাথে, রিঅ্যাক্ট তার নিজস্ব ভার্চুয়াল স্ট্যাক প্রয়োগ করে, যা ফাইবার নোডগুলির একটি লিঙ্কড লিস্ট দ্বারা উপস্থাপিত হয়। এটি রিঅ্যাক্টকে রেন্ডারিং প্রক্রিয়ার উপর সম্পূর্ণ নিয়ন্ত্রণ দেয়।
আপনার কম্পোনেন্ট ট্রিতে প্রতিটি এলিমেন্টের জন্য একটি সংশ্লিষ্ট ফাইবার নোড থাকে। এই নোডগুলি একসাথে লিঙ্ক করে একটি ফাইবার ট্রি তৈরি করে, যা কম্পোনেন্ট ট্রি কাঠামোর প্রতিফলন ঘটায়। একটি ফাইবার নোড গুরুত্বপূর্ণ তথ্য ধারণ করে, যার মধ্যে রয়েছে:
- type and key: কম্পোনেন্টের জন্য শনাক্তকারী, যা আপনি একটি রিঅ্যাক্ট এলিমেন্টে দেখতে পান তার মতো।
- child: এর প্রথম চাইল্ড ফাইবারের একটি পয়েন্টার।
- sibling: এর পরবর্তী সিবলিং ফাইবারের একটি পয়েন্টার।
- return: এর প্যারেন্ট ফাইবারের একটি পয়েন্টার (কাজ শেষ করার পর 'রিটার্ন' পাথ)।
- pendingProps and memoizedProps: পূর্ববর্তী এবং পরবর্তী রেন্ডারের props, যা ডিফারেন্সিং (diffing)-এর জন্য ব্যবহৃত হয়।
- stateNode: আসল DOM নোড, ক্লাস ইনস্ট্যান্স, বা অন্তর্নিহিত প্ল্যাটফর্ম এলিমেন্টের একটি রেফারেন্স।
- effectTag: একটি বিটমাস্ক যা বর্ণনা করে কী কাজ করা দরকার (যেমন, Placement, Update, Deletion)।
এই কাঠামোটি রিঅ্যাক্টকে নেটিভ রিকার্শনের উপর নির্ভর না করে ট্রি ট্রাভার্স করার অনুমতি দেয়। এটি একটি ফাইবারে কাজ শুরু করতে পারে, থামাতে পারে এবং পরে তার জায়গা না হারিয়ে আবার শুরু করতে পারে। কাজ থামানো এবং পুনরায় শুরু করার এই ক্ষমতাটিই সেই foundational mechanism যা রিঅ্যাক্টের সমস্ত কনকারেন্ট ফিচারকে সক্ষম করে।
সিস্টেমের কেন্দ্র: শিডিউলার এবং প্রায়োরিটি লেভেল
যদি ফাইবারগুলি কাজের একক হয়, তবে শিডিউলার হলো সেই মস্তিষ্ক যা সিদ্ধান্ত নেয় কোন কাজটি কখন করতে হবে। রিঅ্যাক্ট একটি স্টেট পরিবর্তনের সাথে সাথে রেন্ডারিং শুরু করে না। পরিবর্তে, এটি আপডেটটিকে একটি প্রায়োরিটি লেভেল বরাদ্দ করে এবং শিডিউলারকে এটি পরিচালনা করতে বলে। শিডিউলার তখন ব্রাউজারের সাথে কাজ করে কাজটি করার সেরা সময় খুঁজে বের করে, এটি নিশ্চিত করে যে এটি আরও গুরুত্বপূর্ণ কাজগুলিকে ব্লক না করে।
প্রাথমিকভাবে, এই সিস্টেমটি কিছু বিচ্ছিন্ন প্রায়োরিটি লেভেল ব্যবহার করত। যদিও আধুনিক বাস্তবায়ন (লেন মডেল) আরও সূক্ষ্ম, এই ধারণাগত স্তরগুলি বোঝা একটি দুর্দান্ত সূচনা বিন্দু:
- ImmediatePriority: এটি সর্বোচ্চ অগ্রাধিকার, যা সিনক্রোনাস আপডেটের জন্য সংরক্ষিত যা অবিলম্বে ঘটতে হবে। এর একটি ক্লাসিক উদাহরণ হলো একটি নিয়ন্ত্রিত ইনপুট (controlled input)। যখন একজন ব্যবহারকারী একটি ইনপুট ক্ষেত্রে টাইপ করেন, তখন UI-কে সেই পরিবর্তনটি অবিলম্বে প্রতিফলিত করতে হবে। যদি এটি কয়েক মিলিসেকেন্ডের জন্যও স্থগিত করা হয়, ইনপুটটি ধীর মনে হবে।
- UserBlockingPriority: এটি সেইসব আপডেটের জন্য যা ব্যবহারকারীর নির্দিষ্ট ইন্টারঅ্যাকশন, যেমন একটি বোতামে ক্লিক করা বা স্ক্রিনে ট্যাপ করা থেকে উদ্ভূত হয়। এগুলি ব্যবহারকারীর কাছে অবিলম্বে মনে হওয়া উচিত তবে প্রয়োজনে খুব অল্প সময়ের জন্য স্থগিত করা যেতে পারে। বেশিরভাগ ইভেন্ট হ্যান্ডলার এই অগ্রাধিকার স্তরে আপডেট ট্রিগার করে।
- NormalPriority: এটি বেশিরভাগ আপডেটের জন্য ডিফল্ট অগ্রাধিকার, যেমন ডেটা ফেচ (`useEffect`) বা নেভিগেশন থেকে উদ্ভূত আপডেট। এই আপডেটগুলির তাৎক্ষণিক হওয়ার প্রয়োজন নেই, এবং রিঅ্যাক্ট ব্যবহারকারীর ইন্টারঅ্যাকশনের সাথে হস্তক্ষেপ এড়াতে এগুলিকে শিডিউল করতে পারে।
- LowPriority: এটি সেইসব আপডেটের জন্য যা সময়-সংবেদনশীল নয়, যেমন অফস্ক্রিন কনটেন্ট রেন্ডার করা বা অ্যানালিটিক্স ইভেন্ট।
- IdlePriority: সর্বনিম্ন অগ্রাধিকার, এমন কাজের জন্য যা কেবল তখনই করা যেতে পারে যখন ব্রাউজার সম্পূর্ণ নিষ্ক্রিয় থাকে। এটি অ্যাপ্লিকেশন কোড দ্বারা সরাসরি খুব কমই ব্যবহৃত হয় তবে অভ্যন্তরীণভাবে লগিং বা ভবিষ্যতের কাজের প্রাক-গণনার মতো জিনিসগুলির জন্য ব্যবহৃত হয়।
রিঅ্যাক্ট আপডেটের প্রেক্ষাপটের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে সঠিক অগ্রাধিকার নির্ধারণ করে। উদাহরণস্বরূপ, একটি `click` ইভেন্ট হ্যান্ডলারের ভিতরের একটি আপডেটকে `UserBlockingPriority` হিসাবে শিডিউল করা হয়, যখন `useEffect`-এর ভিতরের একটি আপডেট সাধারণত `NormalPriority` হয়। এই বুদ্ধিমান, প্রেক্ষাপট-সচেতন অগ্রাধিকারই রিঅ্যাক্টকে বাক্সের বাইরে থেকেই দ্রুত অনুভব করায়।
লেন থিওরি: আধুনিক প্রায়োরিটি মডেল
রিঅ্যাক্টের কনকারেন্ট ফিচারগুলি আরও পরিশীলিত হওয়ার সাথে সাথে, সাধারণ সংখ্যাসূচক অগ্রাধিকার ব্যবস্থা অপর্যাপ্ত প্রমাণিত হয়। এটি বিভিন্ন অগ্রাধিকারের একাধিক আপডেট, ইন্টারাপশন এবং ব্যাচিংয়ের মতো জটিল পরিস্থিতি সুন্দরভাবে পরিচালনা করতে পারছিল না। এটি **লেন মডেল**-এর বিকাশের দিকে পরিচালিত করে।
একটি একক অগ্রাধিকার সংখ্যার পরিবর্তে, ৩১টি "লেন"-এর একটি সেট কল্পনা করুন। প্রতিটি লেন একটি ভিন্ন অগ্রাধিকারকে প্রতিনিধিত্ব করে। এটি একটি বিটমাস্ক হিসাবে প্রয়োগ করা হয়—একটি ৩১-বিটের পূর্ণসংখ্যা যেখানে প্রতিটি বিট একটি লেনের সাথে সঙ্গতিপূর্ণ। এই বিটমাস্ক পদ্ধতিটি অত্যন্ত কার্যকর এবং শক্তিশালী অপারেশনের অনুমতি দেয়:
- একাধিক অগ্রাধিকার উপস্থাপন: একটি একক বিটমাস্ক পেন্ডিং অগ্রাধিকারগুলির একটি সেট উপস্থাপন করতে পারে। উদাহরণস্বরূপ, যদি একটি কম্পোনেন্টে একটি `UserBlocking` আপডেট এবং একটি `Normal` আপডেট উভয়ই পেন্ডিং থাকে, তবে তার `lanes` প্রপার্টিতে সেই দুটি অগ্রাধিকারের জন্য বিটগুলি ১-এ সেট করা থাকবে।
- ওভারল্যাপ পরীক্ষা করা: বিটওয়াইজ অপারেশনগুলি দুটি লেনের সেট ওভারল্যাপ করে কিনা বা একটি সেট অন্যটির সাবসেট কিনা তা পরীক্ষা করা তুচ্ছ করে তোলে। এটি একটি ইনকামিং আপডেট বিদ্যমান কাজের সাথে ব্যাচ করা যাবে কিনা তা নির্ধারণ করতে ব্যবহৃত হয়।
- কাজের অগ্রাধিকার নির্ধারণ: রিঅ্যাক্ট দ্রুত একটি পেন্ডিং লেনের সেটে সর্বোচ্চ-অগ্রাধিকারের লেনটি সনাক্ত করতে পারে এবং শুধুমাত্র সেটিতে কাজ করার সিদ্ধান্ত নিতে পারে, আপাতত নিম্ন-অগ্রাধিকারের কাজ উপেক্ষা করে।
একটি উপমা হতে পারে ৩১টি লেন সহ একটি সুইমিং পুল। একটি জরুরি আপডেট, যেমন একজন প্রতিযোগী সাঁতারু, একটি উচ্চ-অগ্রাধিকারের লেন পায় এবং কোনো বাধা ছাড়াই এগিয়ে যেতে পারে। বেশ কয়েকটি অ-জরুরি আপডেট, যেমন সাধারণ সাঁতারু, একটি নিম্ন-অগ্রাধিকারের লেনে একসাথে ব্যাচ করা হতে পারে। যদি হঠাৎ একজন প্রতিযোগী সাঁতারু আসে, তবে লাইফগার্ডরা (শিডিউলার) অগ্রাধিকারের সাঁতারুকে যেতে দেওয়ার জন্য সাধারণ সাঁতারুদের থামিয়ে দিতে পারে। লেন মডেল রিঅ্যাক্টকে এই জটিল সমন্বয় পরিচালনার জন্য একটি অত্যন্ত দানাদার এবং নমনীয় সিস্টেম দেয়।
দুই-পর্বের রিকনসিলিয়েশন প্রক্রিয়া
রিঅ্যাক্ট ফাইবারের জাদু তার দুই-পর্বের কমিট আর্কিটেকচারের মাধ্যমে বাস্তবায়িত হয়। এই পৃথকীকরণটিই রেন্ডারিংকে বাধাযোগ্য (interruptible) হতে দেয় এবং ভিজ্যুয়াল অসঙ্গতি সৃষ্টি করে না।
পর্ব ১: রেন্ডার/রিকনসিলিয়েশন পর্ব (অ্যাসিনক্রোনাস এবং বাধাযোগ্য)
এখানেই রিঅ্যাক্ট ভারী কাজগুলো করে। কম্পোনেন্ট ট্রির রুট থেকে শুরু করে, রিঅ্যাক্ট একটি `workLoop`-এর মধ্যে ফাইবার নোডগুলি ট্রাভার্স করে। প্রতিটি ফাইবারের জন্য, এটি নির্ধারণ করে যে এটি আপডেট করা দরকার কিনা। এটি আপনার কম্পোনেন্টগুলিকে কল করে, নতুন এলিমেন্টগুলির সাথে পুরানো ফাইবারগুলির পার্থক্য বের করে, এবং সাইড এফেক্টগুলির একটি তালিকা তৈরি করে (যেমন, "এই DOM নোডটি যোগ করুন", "এই অ্যাট্রিবিউটটি আপডেট করুন", "এই কম্পোনেন্টটি সরান")।
এই পর্বের সবচেয়ে গুরুত্বপূর্ণ বৈশিষ্ট্য হলো এটি অ্যাসিনক্রোনাস এবং বাধা দেওয়া যেতে পারে। কয়েকটি ফাইবার প্রসেস করার পর, রিঅ্যাক্ট `shouldYield` নামক একটি অভ্যন্তরীণ ফাংশনের মাধ্যমে পরীক্ষা করে যে তার বরাদ্দকৃত সময় (সাধারণত কয়েক মিলিসেকেন্ড) শেষ হয়ে গেছে কিনা। যদি একটি উচ্চ-অগ্রাধিকারের ইভেন্ট ঘটে (যেমন ব্যবহারকারীর ইনপুট) বা যদি তার সময় শেষ হয়ে যায়, রিঅ্যাক্ট তার কাজ থামিয়ে দেবে, ফাইবার ট্রিতে তার অগ্রগতি সংরক্ষণ করবে এবং ব্রাউজারের মূল থ্রেডে নিয়ন্ত্রণ ফিরিয়ে দেবে। ব্রাউজার আবার ফ্রি হলে, রিঅ্যাক্ট ঠিক যেখান থেকে ছেড়েছিল সেখান থেকেই আবার শুরু করতে পারে।
এই পুরো পর্ব জুড়ে, কোনো পরিবর্তনই DOM-এ ফ্ল্যাশ করা হয় না। ব্যবহারকারী পুরানো, সামঞ্জস্যপূর্ণ UI দেখতে পায়। এটি অত্যন্ত গুরুত্বপূর্ণ—যদি রিঅ্যাক্ট ক্রমবর্ধমানভাবে পরিবর্তন প্রয়োগ করত, তবে ব্যবহারকারী একটি ভাঙা, অর্ধেক রেন্ডার করা ইন্টারফেস দেখতে পেত। সমস্ত মিউটেশন মেমরিতে গণনা এবং সংগ্রহ করা হয়, কমিট পর্বের জন্য অপেক্ষা করে।
পর্ব ২: কমিট পর্ব (সিনক্রোনাস এবং বাধা দেওয়া যায় না)
একবার রেন্ডার পর্বটি পুরো আপডেট হওয়া ট্রির জন্য কোনো বাধা ছাড়াই সম্পন্ন হলে, রিঅ্যাক্ট কমিট পর্বে চলে যায়। এই পর্বে, এটি সংগৃহীত সাইড এফেক্টগুলির তালিকা নেয় এবং সেগুলিকে DOM-এ প্রয়োগ করে।
এই পর্বটি সিনক্রোনাস এবং বাধা দেওয়া যায় না। DOM যাতে অ্যাটমিকভাবে আপডেট হয় তা নিশ্চিত করার জন্য এটি একটি একক, দ্রুত বিস্ফোরণে কার্যকর করা প্রয়োজন। এটি ব্যবহারকারীকে কখনও একটি অসামঞ্জস্যপূর্ণ বা আংশিকভাবে আপডেট হওয়া UI দেখতে বাধা দেয়। এই সময়েই রিঅ্যাক্ট `componentDidMount` এবং `componentDidUpdate`-এর মতো লাইফসাইকেল মেথড এবং `useLayoutEffect` হুক চালায়। যেহেতু এটি সিনক্রোনাস, তাই আপনার `useLayoutEffect`-এ দীর্ঘ সময় ধরে চলা কোড এড়ানো উচিত কারণ এটি পেইন্টিং ব্লক করতে পারে।
কমিট পর্ব সম্পন্ন হওয়ার পর এবং DOM আপডেট হওয়ার পর, রিঅ্যাক্ট `useEffect` হুকগুলিকে অ্যাসিনক্রোনাসভাবে চালানোর জন্য শিডিউল করে। এটি নিশ্চিত করে যে `useEffect`-এর ভিতরের কোনো কোড (যেমন ডেটা ফেচিং) ব্রাউজারকে আপডেট হওয়া UI স্ক্রিনে পেইন্ট করতে বাধা দেয় না।
ব্যবহারিক প্রভাব এবং API নিয়ন্ত্রণ
তত্ত্ব বোঝা দারুণ, কিন্তু বিশ্বব্যাপী দলগুলির ডেভেলপাররা কীভাবে এই শক্তিশালী সিস্টেমটিকে কাজে লাগাতে পারে? রিঅ্যাক্ট ১৮ বেশ কয়েকটি API চালু করেছে যা ডেভেলপারদের রেন্ডারিং অগ্রাধিকারের উপর সরাসরি নিয়ন্ত্রণ দেয়।
স্বয়ংক্রিয় ব্যাচিং
রিঅ্যাক্ট ১৮-এ, সমস্ত স্টেট আপডেট স্বয়ংক্রিয়ভাবে ব্যাচ করা হয়, সেগুলি কোথা থেকে উদ্ভূত হয়েছে তা নির্বিশেষে। আগে, শুধুমাত্র রিঅ্যাক্ট ইভেন্ট হ্যান্ডলারের ভিতরের আপডেটগুলি ব্যাচ করা হতো। প্রমিস, `setTimeout`, বা নেটিভ ইভেন্ট হ্যান্ডলারের ভিতরের আপডেটগুলি প্রতিটি একটি পৃথক রি-রেন্ডার ট্রিগার করত। এখন, শিডিউলারকে ধন্যবাদ, রিঅ্যাক্ট একটি "টিক" অপেক্ষা করে এবং সেই টিকের মধ্যে ঘটা সমস্ত স্টেট আপডেটকে একটি একক, অপ্টিমাইজড রি-রেন্ডারে ব্যাচ করে। এটি ডিফল্টরূপে অপ্রয়োজনীয় রেন্ডার কমায় এবং পারফরম্যান্স উন্নত করে।
`startTransition` API
রেন্ডারিং অগ্রাধিকার নিয়ন্ত্রণের জন্য এটি সম্ভবত সবচেয়ে গুরুত্বপূর্ণ API। `startTransition` আপনাকে একটি নির্দিষ্ট স্টেট আপডেটকে অ-জরুরি বা "ট্রানজিশন" হিসাবে চিহ্নিত করতে দেয়।
একটি সার্চ ইনপুট ফিল্ড কল্পনা করুন। যখন ব্যবহারকারী টাইপ করে, দুটি জিনিস ঘটতে হবে: 1. নতুন অক্ষর দেখানোর জন্য ইনপুট ফিল্ডটিকে অবশ্যই আপডেট করতে হবে (উচ্চ অগ্রাধিকার)। 2. সার্চ ফলাফলের একটি তালিকা ফিল্টার করে পুনরায় রেন্ডার করতে হবে, যা একটি ধীর অপারেশন হতে পারে (নিম্ন অগ্রাধিকার)।
`startTransition` ছাড়া, উভয় আপডেটেরই একই অগ্রাধিকার থাকত, এবং একটি ধীর-রেন্ডারিং তালিকা ইনপুট ফিল্ডটিকে ধীর করে দিতে পারত, যা একটি খারাপ ব্যবহারকারীর অভিজ্ঞতা তৈরি করত। তালিকা আপডেটটি `startTransition`-এ মুড়ে দিয়ে, আপনি রিঅ্যাক্টকে বলছেন: "এই আপডেটটি জরুরি নয়। আপনি নতুনটি প্রস্তুত করার সময় ব্যবহারকারীকে পুরানো তালিকাটি কিছুক্ষণ দেখানো ঠিক আছে। ইনপুট ফিল্ডটিকে প্রতিক্রিয়াশীল করার উপর অগ্রাধিকার দিন।"
এখানে একটি ব্যবহারিক উদাহরণ দেওয়া হলো:
সার্চ ফলাফল লোড হচ্ছে...
import { useState, useTransition } from 'react';
function SearchPage() {
const [isPending, startTransition] = useTransition();
const [inputValue, setInputValue] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const handleInputChange = (e) => {
// উচ্চ-অগ্রাধিকারের আপডেট: ইনপুট ফিল্ডটি অবিলম্বে আপডেট করুন
setInputValue(e.target.value);
// নিম্ন-অগ্রাধিকারের আপডেট: ধীর স্টেট আপডেটটি একটি ট্রানজিশনে মুড়ে দিন
startTransition(() => {
setSearchQuery(e.target.value);
});
};
return (
এই কোডে, `setInputValue` একটি উচ্চ-অগ্রাধিকারের আপডেট, যা নিশ্চিত করে যে ইনপুট কখনও ধীর হয় না। `setSearchQuery`, যা সম্ভাব্য ধীর `SearchResults` কম্পোনেন্টকে পুনরায় রেন্ডার করতে ট্রিগার করে, তাকে একটি ট্রানজিশন হিসাবে চিহ্নিত করা হয়েছে। রিঅ্যাক্ট এই ট্রানজিশনকে বাধা দিতে পারে যদি ব্যবহারকারী আবার টাইপ করে, পুরানো রেন্ডার কাজটি ফেলে দিয়ে নতুন কোয়েরি দিয়ে নতুন করে শুরু করে। `useTransition` হুক দ্বারা প্রদত্ত `isPending` ফ্ল্যাগটি এই ট্রানজিশনের সময় ব্যবহারকারীকে একটি লোডিং স্টেট দেখানোর একটি সুবিধাজনক উপায়।
`useDeferredValue` হুক
`useDeferredValue` একই রকম ফলাফল অর্জনের জন্য একটি ভিন্ন উপায় সরবরাহ করে। এটি আপনাকে ট্রি-এর একটি অ-গুরুত্বপূর্ণ অংশের রি-রেন্ডারিং স্থগিত করতে দেয়। এটি একটি ডিবাউন্স প্রয়োগ করার মতো, কিন্তু অনেক বেশি স্মার্ট কারণ এটি সরাসরি রিঅ্যাক্টের শিডিউলারের সাথে সমন্বিত।
এটি একটি ভ্যালু নেয় এবং সেই ভ্যালুর একটি নতুন কপি ফেরত দেয় যা একটি রেন্ডারের সময় মূলটির থেকে "পিছিয়ে থাকবে"। যদি বর্তমান রেন্ডারটি একটি জরুরি আপডেট (যেমন ব্যবহারকারীর ইনপুট) দ্বারা ট্রিগার হয়ে থাকে, রিঅ্যাক্ট প্রথমে পুরানো, ডিফারড ভ্যালু দিয়ে রেন্ডার করবে এবং তারপরে নতুন ভ্যালু দিয়ে একটি রি-রেন্ডার নিম্ন অগ্রাধিকার স্তরে শিডিউল করবে।
আসুন `useDeferredValue` ব্যবহার করে সার্চের উদাহরণটি রিফ্যাক্টর করি:
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const handleInputChange = (e) => {
setQuery(e.target.value);
};
return (
এখানে, `input` সর্বদা সর্বশেষ `query`-এর সাথে আপ-টু-ডেট থাকে। তবে, `SearchResults` `deferredQuery` গ্রহণ করে। যখন ব্যবহারকারী দ্রুত টাইপ করে, `query` প্রতিটি কীস্ট্রোকে আপডেট হয়, কিন্তু `deferredQuery` তার পূর্ববর্তী মান ধরে রাখবে যতক্ষণ না রিঅ্যাক্টের কাছে সময় থাকে। এটি কার্যকরভাবে তালিকার রেন্ডারিংকে ডি-প্রায়োরিটাইজ করে, UI-কে সাবলীল রাখে।
প্রায়োরিটি লেনগুলি কল্পনা করা: একটি মানসিক মডেল
এই মানসিক মডেলটিকে আরও দৃঢ় করতে আসুন একটি জটিল পরিস্থিতি পর্যালোচনা করি। একটি সোশ্যাল মিডিয়া ফিড অ্যাপ্লিকেশন কল্পনা করুন:
- প্রাথমিক অবস্থা: ব্যবহারকারী পোস্টের একটি দীর্ঘ তালিকার মধ্য দিয়ে স্ক্রল করছেন। এটি ভিউতে আসা নতুন আইটেমগুলি রেন্ডার করার জন্য `NormalPriority` আপডেট ট্রিগার করে।
- উচ্চ-অগ্রাধিকারের বাধা: স্ক্রল করার সময়, ব্যবহারকারী একটি পোস্টের মন্তব্য বক্সে একটি মন্তব্য টাইপ করার সিদ্ধান্ত নেয়। এই টাইপিং অ্যাকশনটি ইনপুট ফিল্ডের জন্য `ImmediatePriority` আপডেট ট্রিগার করে।
- কনকারেন্ট নিম্ন-অগ্রাধিকারের কাজ: মন্তব্য বক্সে এমন একটি বৈশিষ্ট্য থাকতে পারে যা ফরম্যাট করা পাঠ্যের একটি লাইভ প্রিভিউ দেখায়। এই প্রিভিউ রেন্ডার করা ধীর হতে পারে। আমরা প্রিভিউর জন্য স্টেট আপডেটটিকে একটি `startTransition`-এ মুড়ে দিতে পারি, এটিকে একটি `LowPriority` আপডেট তৈরি করে।
- ব্যাকগ্রাউন্ড আপডেট: একই সাথে, নতুন পোস্টের জন্য একটি ব্যাকগ্রাউন্ড `fetch` কল সম্পন্ন হয়, যা ফিডের শীর্ষে একটি "New Posts Available" ব্যানার যোগ করার জন্য আরেকটি `NormalPriority` স্টেট আপডেট ট্রিগার করে।
রিঅ্যাক্টের শিডিউলার কীভাবে এই ট্র্যাফিক পরিচালনা করবে তা এখানে দেওয়া হলো:
- রিঅ্যাক্ট অবিলম্বে `NormalPriority` স্ক্রল রেন্ডারিং কাজটি থামিয়ে দেয়।
- এটি `ImmediatePriority` ইনপুট আপডেটগুলি তাত্ক্ষণিকভাবে পরিচালনা করে। ব্যবহারকারীর টাইপিং সম্পূর্ণ প্রতিক্রিয়াশীল মনে হয়।
- এটি ব্যাকগ্রাউন্ডে `LowPriority` মন্তব্য প্রিভিউ রেন্ডারের কাজ শুরু করে।
- `fetch` কলটি ফিরে আসে, ব্যানারের জন্য একটি `NormalPriority` আপডেট শিডিউল করে। যেহেতু এটির অগ্রাধিকার মন্তব্য প্রিভিউর চেয়ে বেশি, তাই রিঅ্যাক্ট প্রিভিউ রেন্ডারিং থামিয়ে দেবে, ব্যানার আপডেটে কাজ করবে, এটিকে DOM-এ কমিট করবে এবং তারপরে যখন তার নিষ্ক্রিয় সময় থাকবে তখন প্রিভিউ রেন্ডারিং পুনরায় শুরু করবে।
- একবার সমস্ত ব্যবহারকারীর ইন্টারঅ্যাকশন এবং উচ্চ-অগ্রাধিকারের কাজগুলি সম্পন্ন হয়ে গেলে, রিঅ্যাক্ট মূল `NormalPriority` স্ক্রল রেন্ডারিং কাজটি যেখান থেকে ছেড়েছিল সেখান থেকেই পুনরায় শুরু করে।
কাজের এই গতিশীল থামা, অগ্রাধিকার দেওয়া এবং পুনরায় শুরু করাই হলো প্রায়োরিটি লেন ম্যানেজমেন্টের মূল সারাংশ। এটি নিশ্চিত করে যে ব্যবহারকারীর পারফরম্যান্সের ধারণা সর্বদা অপ্টিমাইজ করা হয় কারণ সবচেয়ে গুরুত্বপূর্ণ ইন্টারঅ্যাকশনগুলি কম গুরুত্বপূর্ণ ব্যাকগ্রাউন্ড টাস্ক দ্বারা কখনই ব্লক হয় না।
বৈশ্বিক প্রভাব: শুধু গতির বাইরেও
রিঅ্যাক্টের কনকারেন্ট রেন্ডারিং মডেলের সুবিধাগুলি কেবল অ্যাপ্লিকেশনগুলিকে দ্রুত অনুভব করানোর মধ্যেই সীমাবদ্ধ নয়। বিশ্বব্যাপী ব্যবহারকারীদের জন্য মূল ব্যবসা এবং পণ্য মেট্রিক্সে এর একটি বাস্তব প্রভাব রয়েছে।
- অ্যাক্সেসিবিলিটি: একটি প্রতিক্রিয়াশীল UI একটি অ্যাক্সেসিবল UI। যখন একটি ইন্টারফেস জমে যায়, তখন এটি সমস্ত ব্যবহারকারীর জন্য বিভ্রান্তিকর এবং অব্যবহারযোগ্য হতে পারে, তবে এটি বিশেষত তাদের জন্য সমস্যাযুক্ত যারা স্ক্রিন রিডারের মতো সহায়ক প্রযুক্তির উপর নির্ভর করে, যা প্রসঙ্গ হারাতে পারে বা অপ্রতিক্রিয়াশীল হয়ে যেতে পারে।
- ব্যবহারকারী ধরে রাখা: একটি প্রতিযোগিতামূলক ডিজিটাল পরিবেশে, পারফরম্যান্স একটি বৈশিষ্ট্য। ধীর, জ্যাঙ্কি অ্যাপ্লিকেশন ব্যবহারকারীর হতাশা, উচ্চতর বাউন্স রেট এবং কম এনগেজমেন্টের দিকে পরিচালিত করে। একটি সাবলীল অভিজ্ঞতা আধুনিক সফ্টওয়্যারের একটি মূল প্রত্যাশা।
- ডেভেলপার অভিজ্ঞতা: লাইব্রেরির মধ্যেই এই শক্তিশালী শিডিউলিং প্রিমিটিভগুলি তৈরি করে, রিঅ্যাক্ট ডেভেলপারদের আরও ঘোষণামূলকভাবে জটিল, পারফরম্যান্ট UI তৈরি করতে দেয়। জটিল ডিবাউন্সিং, থ্রটলিং বা `requestIdleCallback` লজিক ম্যানুয়ালি প্রয়োগ করার পরিবর্তে, ডেভেলপাররা `startTransition`-এর মতো API ব্যবহার করে রিঅ্যাক্টকে তাদের উদ্দেশ্য সংকেত দিতে পারে, যা পরিষ্কার, আরও রক্ষণাবেক্ষণযোগ্য কোডের দিকে পরিচালিত করে।
বিশ্বব্যাপী ডেভেলপমেন্ট টিমের জন্য কার্যকরী পদক্ষেপ
- কনকারেন্সি গ্রহণ করুন: নিশ্চিত করুন যে আপনার দল রিঅ্যাক্ট ১৮ ব্যবহার করছে এবং নতুন কনকারেন্ট বৈশিষ্ট্যগুলি বোঝে। এটি একটি প্যারাডাইম শিফট।
- ট্রানজিশন শনাক্ত করুন: আপনার অ্যাপ্লিকেশন অডিট করে এমন কোনো UI আপডেট খুঁজুন যা জরুরি নয়। সংশ্লিষ্ট স্টেট আপডেটগুলিকে `startTransition`-এ মুড়ে দিন যাতে সেগুলি আরও গুরুত্বপূর্ণ ইন্টারঅ্যাকশনগুলিকে ব্লক করতে না পারে।
- ভারী রেন্ডার স্থগিত করুন: যে কম্পোনেন্টগুলি রেন্ডার করতে ধীর এবং দ্রুত পরিবর্তনশীল ডেটার উপর নির্ভর করে, সেগুলির রি-রেন্ডারিংকে ডি-প্রায়োরিটাইজ করতে এবং বাকি অ্যাপ্লিকেশনটিকে দ্রুত রাখতে `useDeferredValue` ব্যবহার করুন।
- প্রোফাইল এবং পরিমাপ করুন: আপনার কম্পোনেন্টগুলি কীভাবে রেন্ডার হয় তা ভিজ্যুয়ালাইজ করতে রিঅ্যাক্ট ডেভটুলস প্রোফাইলার ব্যবহার করুন। প্রোফাইলারটি কনকারেন্ট রিঅ্যাক্টের জন্য আপডেট করা হয়েছে এবং এটি আপনাকে সনাক্ত করতে সাহায্য করতে পারে কোন আপডেটগুলি বাধাগ্রস্ত হচ্ছে এবং কোনটি পারফরম্যান্সের বাধা সৃষ্টি করছে।
- শিক্ষাদান এবং প্রচার করুন: আপনার দলের মধ্যে এই ধারণাগুলি প্রচার করুন। পারফরম্যান্ট অ্যাপ্লিকেশন তৈরি করা একটি সম্মিলিত দায়িত্ব, এবং রিঅ্যাক্টের শিডিউলার সম্পর্কে একটি সাধারণ বোঝাপড়া অপ্টিমাল কোড লেখার জন্য অত্যন্ত গুরুত্বপূর্ণ।
উপসংহার
রিঅ্যাক্ট ফাইবার এবং এর প্রায়োরিটি-ভিত্তিক শিডিউলার ফ্রন্ট-এন্ড ফ্রেমওয়ার্কের বিবর্তনে একটি বিশাল লাফকে প্রতিনিধিত্ব করে। আমরা ব্লকিং, সিনক্রোনাস রেন্ডারিংয়ের বিশ্ব থেকে সমবায়িক, বাধাযোগ্য শিডিউলিংয়ের একটি নতুন প্যারাডাইমে চলে এসেছি। কাজকে পরিচালনাযোগ্য ফাইবার খণ্ডে বিভক্ত করে এবং সেই কাজের অগ্রাধিকার নির্ধারণের জন্য একটি পরিশীলিত লেন মডেল ব্যবহার করে, রিঅ্যাক্ট নিশ্চিত করতে পারে যে ব্যবহারকারী-মুখী ইন্টারঅ্যাকশনগুলি সর্বদা প্রথমে পরিচালনা করা হয়, এমন অ্যাপ্লিকেশন তৈরি করে যা পটভূমিতে জটিল কাজ সম্পাদন করার সময়ও সাবলীল এবং তাত্ক্ষণিক অনুভূত হয়।
ডেভেলপারদের জন্য, ট্রানজিশন এবং ডিফারড ভ্যালুর মতো ধারণাগুলিতে দক্ষতা অর্জন করা আর একটি ঐচ্ছিক অপ্টিমাইজেশন নয়—এটি আধুনিক, উচ্চ-পারফরম্যান্স ওয়েব অ্যাপ্লিকেশন তৈরির জন্য একটি মূল দক্ষতা। রিঅ্যাক্টের প্রায়োরিটি লেন ম্যানেজমেন্ট বোঝা এবং ব্যবহার করে, আপনি বিশ্বব্যাপী দর্শকদের কাছে একটি উন্নত ব্যবহারকারীর অভিজ্ঞতা প্রদান করতে পারেন, এমন ইন্টারফেস তৈরি করতে পারেন যা কেবল কার্যকরীই নয়, ব্যবহার করতে সত্যিই আনন্দদায়ক।